package com.mt.restaurant.service.material.impl;

import com.mt.restaurant.dao.material.MaterialDao;
import com.mt.restaurant.dao.material.StoreMaterialDao;
import com.mt.restaurant.dao.material.StockRecordDao;
import com.mt.common.core.exception.BusinessException;
import com.mt.common.core.web.base.PageDTO;
import com.mt.common.core.web.base.PageResultDTO;
import com.mt.common.core.web.base.BaseEntity;
import com.mt.common.core.web.BaseService;
import com.mt.restaurant.entity.material.*;
import com.mt.restaurant.service.material.MaterialOrderService;
import com.mt.restaurant.service.material.MaterialService;
import com.mt.restaurant.service.material.OrderMaterialRelationService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.mt.restaurant.service.material.StoreMaterialService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Transactional
public class StoreMaterialServiceBean extends BaseService implements StoreMaterialService {

    private static Logger logger = LogManager.getLogger();

    @Autowired
    private StoreMaterialDao storeMaterialDao;

    @Autowired
    private StockRecordDao stockRecordDao;

    @Resource
    private RedisTemplate<String, List<StoreMaterial>> redisTemplate;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    private OrderMaterialRelationService orderMaterialRelationService;

    @Autowired
    private MaterialOrderService materialOrderService;

    @Autowired
    private MaterialDao materialDao;

    /**
     * 根据分页参数查询仓库材料关系集合
     *
     * @param pageDTO 分页条件
     */
    @Override
    public PageResultDTO findStoreMaterials(PageDTO pageDTO) {
        if (pageDTO.getCurrentPage() != null && pageDTO.getPageSize() != null) {
            pageDTO.setStartIndex((pageDTO.getCurrentPage() - 1) * pageDTO.getPageSize());
        }
        //TODO:请在此校验参数的合法性
        this.validateFindStoreMaterials(pageDTO);
        List<StoreMaterial> storeMaterialDTOS = this.storeMaterialDao.findStoreMaterials(pageDTO);
        Long totalCount = this.storeMaterialDao.findStoreMaterialTotalCount(pageDTO);

        PageResultDTO pageResultDTO = new PageResultDTO();
        pageResultDTO.setTotalCount(totalCount);
        pageResultDTO.setDatas(storeMaterialDTOS);

        return pageResultDTO;
    }

    /**
     * 查询全部仓库材料关系集合
     */
    @Override
    public List<StoreMaterial> findAllStoreMaterials() {
        return this.storeMaterialDao.findAllStoreMaterials();
    }

    /**
     * 查询所有仓库材料关系集合(只提取ID 和 Name)
     */
    @Override
    public List<StoreMaterial> findAllStoreMaterialsWithIdName() {
        //TODO:请在此校验参数的合法性
        this.validateFindAllStoreMaterialsWithIdName();
        return this.storeMaterialDao.findAllStoreMaterialsWithIdName();
    }

    /**
     * 根据名称查询仓库材料关系集合(只提取ID 和 Name)
     *
     * @param storeMaterialName 名称
     */
    @Override
    public List<StoreMaterial> findStoreMaterialsWithIdNameByName(String storeMaterialName) {
        //TODO:请在此校验参数的合法性
        this.validateFindStoreMaterialsWithIdNameByName(storeMaterialName);
        //TODO:缓存取对应参数
        Set<String> keys = stringRedisTemplate.keys("searchData:StoreMaterial_where_storeMaterialName_" + storeMaterialName);
        List<StoreMaterial> storeMaterials = new ArrayList<>();
        if (keys.isEmpty()) {
            storeMaterials = this.storeMaterialDao.findStoreMaterialsWithIdNameByName(storeMaterialName);
            redisTemplate.opsForValue().set("searchData:StoreMaterial_where_storeMaterialName_" + storeMaterialName, storeMaterials, 30, TimeUnit.DAYS);
        } else {
            storeMaterials = redisTemplate.opsForValue().get("searchData:StoreMaterial_where_storeMaterialName_" + storeMaterialName);
        }
        return storeMaterials;
    }

    /**
     * 根据ID查询指定的仓库材料关系(只提取ID 和 Name)
     *
     * @param storeMaterialId Id
     */
    @Override
    public StoreMaterial findStoreMaterialsWithIdNameById(Long storeMaterialId) {
        //TODO:请在此校验参数的合法性
        this.validateFindStoreMaterialsWithIdNameById(storeMaterialId);
        return this.storeMaterialDao.findStoreMaterialsWithIdNameById(storeMaterialId);
    }

    /**
     * 根据ID查询指定的仓库材料关系
     *
     * @param storeMaterialId Id
     */
    @Override
    public StoreMaterial findStoreMaterial(Long storeMaterialId) {
        //TODO:请在此校验参数的合法性
        this.validateFindStoreMaterial(storeMaterialId);
        return this.storeMaterialDao.findStoreMaterial(storeMaterialId);
    }

    /**
     * 根据ID查询指定的仓库材料关系(包含外键)
     *
     * @param storeMaterialId Id
     */
    @Override
    public StoreMaterial findStoreMaterialWithForeignName(Long storeMaterialId) {
        //TODO:请在此校验参数的合法性
        this.validateFindStoreMaterialWithForeignName(storeMaterialId);
        return this.storeMaterialDao.findStoreMaterialWithForeignName(storeMaterialId);
    }

    /**
     * 新增仓库材料关系
     *
     * @param operation     操作类型
     * @param storeMaterial 实体对象
     */
    @Override
    public StoreMaterial saveStoreMaterial(Integer operation, StoreMaterial storeMaterial) {
        //TODO:请在此校验参数的合法性
        this.validateSaveStoreMaterial(storeMaterial);
        StoreMaterial storeMaterialdb = this.storeMaterialDao.findStoreMaterialByMaterialId(storeMaterial.getMaterialId());
        Double changedQuantity = storeMaterial.getQuantity();
        //更新材料价格
        materialDao.updateMaterialMaxPriceById(storeMaterial.getMaterialId(), storeMaterial.getPrice());
        if (storeMaterialdb != null) {
            Double quantity = storeMaterialdb.getQuantity();
            //TODO:填充公共参数
            this.setSavePulicColumns(storeMaterialdb);
            //存在材料id相同的记录，修改记录
            if (operation == 1 || operation == 0) {
                //入库
                storeMaterialdb.setQuantity(quantity + changedQuantity);

            } else if (operation == 2 || operation == 3) {
                //领用或损耗
                if (quantity - changedQuantity < 0) {
                    String error = "库存不足，请检查后重新输入";
                    throw new BusinessException(error);
                }
                storeMaterialdb.setQuantity(quantity - changedQuantity);
            }
            storeMaterialdb = changeStoreMaterial(storeMaterialdb);
            saveStockRecord(operation, changedQuantity, storeMaterialdb);
            return storeMaterialdb;
        } else {
            //不存在材料id相同的记录，直接保存
            //TODO:填充公共参数
            this.setSavePulicColumns(storeMaterial);
            if (operation == 1 || operation == 0) {
                //				入库或退库
            } else if (operation == 2) {
                //				不能有领用操作，抛出异常
                String error = "领用材料出错，库存不存在";
                throw new BusinessException(error);
            } else {
                String error = "修改保存仓库材料关系出错，operation只能为0-3的整型";
                throw new BusinessException(error);
            }
            Long rows = this.storeMaterialDao.saveStoreMaterial(storeMaterial);
            saveStockRecord(operation, changedQuantity, storeMaterial);
            if (rows != 1) {
                String error = "新增保存仓库材料关系出错，数据库应该返回1,但返回了 " + rows;
                throw new BusinessException(error);
            }
            return storeMaterial;
        }

    }

    /**
     * 增加一条库存记录
     *
     * @param storeMaterial 实体对象
     */
    public void saveStockRecord(Integer operation, Double quantity, StoreMaterial storeMaterial) {
        StockRecord stockRecord = new StockRecord();
        stockRecord.newStockRecordByStoreMaterial(operation, quantity, storeMaterial);
        //TODO:填充公共参数
        this.setSavePulicColumns(stockRecord);
        Long rows = this.stockRecordDao.saveStockRecord(stockRecord);
        if (rows != 1) {
            String error = "新增保存仓库记录出错，数据库应该返回1,但返回了 " + rows;
            throw new BusinessException(error);
        }
    }

    public void saveStoreMaterialsByOrderMaterial(List<StoreMaterial> storeMaterialsForSave, List<StoreMaterial> storeMaterialsForUpdate) {
        if (storeMaterialsForSave != null && storeMaterialsForSave.size() != 0) {
            storeMaterialsForSave.forEach(item -> {
                Long rows = storeMaterialDao.saveStoreMaterials(item);
                if (rows != 1) {
                    String error = "新增保存材料库存出错，数据库应该返回1,但返回了 " + rows;
                    throw new BusinessException(error);
                }
                rows = materialDao.updateMaterialPriceById(item.getMaterialId(), item.getPrice());
                if (rows != 1) {
                    String error = "新增保存材料最高价格出错，数据库应该返回1,但返回了 " + rows;
                    throw new BusinessException(error);
                }
                saveStockRecord(1, item.getQuantity(), item);
            });
        }
            if (storeMaterialsForUpdate != null && storeMaterialsForUpdate.size() != 0) {
            storeMaterialsForUpdate.forEach(item -> {
                Long rows = storeMaterialDao.updateStoreMaterialByMaterialid(item);
                if (rows != 1) {
                    String error = "修改材料库存出错，数据库应该返回1,但返回了 " + rows;
                    throw new BusinessException(error);
                }
                rows = materialDao.updateMaterialMaxPriceById(item.getMaterialId(), item.getPrice());
                saveStockRecord(1, item.getQuantity(), item);
            });
        }

    }

    /**
     * 改变材料并提交
     *
     * @param storeMaterial 实体对象
     */
    public StoreMaterial changeStoreMaterial(StoreMaterial storeMaterial) {
        //TODO:请在此校验参数的合法性
        this.validateUpdateStoreMaterial(storeMaterial);
        Long rows = this.storeMaterialDao.updateStoreMaterial(storeMaterial);
        if (rows != 1) {
            String error = "修改保存仓库材料关系出错，数据库应该返回1,但返回了 " + rows + ",数据可能被删除";
            throw new BusinessException(error);
        }
        return storeMaterial;
    }

    /**
     * 更新仓库材料关系
     *
     * @param operation     操作类型
     * @param storeMaterial 实体对象
     */
    @Override
    public StoreMaterial updateStoreMaterial(Integer operation, StoreMaterial storeMaterial) {
        //TODO:请在此校验参数的合法性
        this.validateUpdateStoreMaterial(storeMaterial);
        StoreMaterial storeMaterialdb = this.storeMaterialDao.findStoreMaterialByMaterialId(storeMaterial.getMaterialId());
        Double quantity = storeMaterialdb.getQuantity();
        Double changedQuantity = storeMaterial.getQuantity();
        Double curPrice = storeMaterial.getPrice();
        Double prePrice = storeMaterialdb.getPrice();

//        if((operation == 0||operation == 2)&&(quantity - changedQuantity == 0)){
//            this.deleteStoreMaterial(storeMaterial.getEid());
//            storeMaterialdb.setQuantity(quantity - changedQuantity);
//            return storeMaterialdb;
//        }
        //存在材料id相同的记录，修改记录
        if (operation == 1) {
//				入库
            if(curPrice>prePrice){
                storeMaterialdb.setPrice(curPrice);
            }
            storeMaterialdb.setQuantity(quantity + changedQuantity);
        } else if (operation == 0) {
//				退库
            storeMaterialdb.setQuantity(quantity + changedQuantity);

        } else if (operation == 2 || operation == 3) {
//				领用或者损耗
            if (quantity - changedQuantity < 0) {
                String error = "库存不足，请检查后重新输入";
                throw new BusinessException(error);
            }
            storeMaterialdb.setQuantity(quantity - changedQuantity);
        } else {
            String error = "新增保存仓库材料关系出错，operation只能为0-3的整型";
            throw new BusinessException(error);
        }
        Long rows = this.storeMaterialDao.updateStoreMaterial(storeMaterialdb);
        saveStockRecord(operation, changedQuantity, storeMaterialdb);
        if (rows != 1) {
            String error = "修改保存仓库材料关系出错，数据库应该返回1,但返回了 " + rows + ",数据可能被删除";
            throw new BusinessException(error);
        }
        return storeMaterialdb;
    }

    @Override
    public StoreMaterial updateStoreMaterialByOrder(List<MaterialOrder> materialOrderList) {
        if (materialOrderList == null) {
            return null;
        }
        //根据订单获取订单材料
        List<OrderMaterialRelation> orderMaterialRelationList = orderMaterialRelationService.findOrderMaterialsByOrder(materialOrderList);

        List<StoreMaterial> storeMaterials = getStoreMaterialByOrderMaterial(orderMaterialRelationList);

        List<StoreMaterial> storeMaterialsForSave = new ArrayList<>();
        List<StoreMaterial> storeMaterialsForUpdate = new ArrayList<>();

        Map<String, Object> filter = new HashMap<>();
        PageDTO pageDTO = PageDTO.create(filter);
        List<StoreMaterial> dbStoreMaterials = (List<StoreMaterial>) findStoreMaterials(pageDTO).getDatas();

        if (dbStoreMaterials == null || dbStoreMaterials.size() == 0) {
            saveStoreMaterialsByOrderMaterial(storeMaterials, null);
            return null;
        }

        Long[] materialIds = dbStoreMaterials.stream().map(StoreMaterial::getMaterialId).toArray(Long[]::new);

        for (StoreMaterial storeMaterial : storeMaterials) {
            if (Arrays.asList(materialIds).contains(storeMaterial.getMaterialId())) {
                storeMaterialsForUpdate.add(storeMaterial);
                System.out.println("已经有了");
            } else {
                storeMaterialsForSave.add(storeMaterial);
                System.out.println("还没有");
            }

        }
        saveStoreMaterialsByOrderMaterial(storeMaterialsForSave, storeMaterialsForUpdate);
        System.out.println("条数据，更新了" + storeMaterialsForUpdate.size() +
                "条，新增了" + storeMaterialsForSave.size() + "条");
        materialOrderService.updateMaterialOrdersStatus(materialOrderList, 5);
        return null;
    }

    @Override
    public void updateStoreMaterialPriceById(Long materialId, Double price) {
        Long rows = this.storeMaterialDao.updateStoreMaterialPriceById(materialId, price);
        if (rows != 1) {
            String error = "修改保存仓库材料价格出错，数据库应该返回1,但返回了 " + rows + ",数据可能被删除";
            throw new BusinessException(error);
        }
    }

    /**
     * 根据ID删除仓库材料关系
     *
     * @param storeMaterialId ID
     */
    @Override
    public void deleteStoreMaterial(Long storeMaterialId) {
        //TODO:请在此校验参数的合法性
        this.validateDeleteStoreMaterial(storeMaterialId);

        Map<Class<? extends BaseEntity>, EntityUsage> entityUsageMap = this.checkForeignEntity(StoreMaterial.class, storeMaterialId);
        if (entityUsageMap != null && entityUsageMap.size() > 0) {
            StringBuilder errors = new StringBuilder();
            errors.append("计划删除的数据正在被以下数引用\n");
            for (EntityUsage entityUsage : entityUsageMap.values()) {
                errors.append("\t").append(entityUsage.getEntityLabel()).append("\n");
                for (Map.Entry<Long, String> entry : entityUsage.getUsageIdNames().entrySet()) {
                    errors.append("\t\t").append(entry.getKey()).append("\t").append(entry.getValue()).append("\n");
                }
            }
            errors.append("，不能删除，请检查处理后再删除");
            throw new BusinessException(errors.toString());
        }

        Long rows = this.storeMaterialDao.deleteStoreMaterial(storeMaterialId);
        if (rows != 1) {
            String error = "删除仓库材料关系出错，数据可能已经被删除";
            throw new BusinessException(error);
        }
    }

    public List<StoreMaterial> getStoreMaterialByOrderMaterial(List<OrderMaterialRelation> orderMaterialRelationList) {
        List<StoreMaterial> storeMaterialList = new ArrayList<>();
        orderMaterialRelationList.forEach(item -> {
            StoreMaterial storeMaterial = new StoreMaterial();
            setSavePulicColumns(storeMaterial);
            storeMaterial.setName(item.getSupplyMaterialName());
            storeMaterial.setMaterialId(item.getMaterialId());
            storeMaterial.setPrice(item.getPrice());
            storeMaterial.setQuantity(item.getAmount());
            storeMaterialList.add(storeMaterial);
        });
        return storeMaterialList;
    }

    //TODO:---------------验证-------------------

    private void validateFindStoreMaterials(PageDTO pageDTO) {
        //TODO:请使用下面方法添加数据过滤条件
        //		pageDTO.addFilter("creatorId",this.getLoginUserId());
        //TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateStoreMaterial()写法
    }

    private void validateFindStoreMaterialsWithIdNameByName(String storeMaterialName) {
        //TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateStoreMaterial()写法
    }


    private void validateFindAllStoreMaterialsWithIdName() {
        //TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateStoreMaterial()写法
    }

    private void validateFindStoreMaterialsWithIdNameById(Long storeMaterialId) {
        //TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateStoreMaterial()写法
    }

    private void validateFindStoreMaterial(Long storeMaterialId) {
        //TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateStoreMaterial()写法
    }

    private void validateFindStoreMaterialWithForeignName(Long storeMaterialId) {
        //TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateStoreMaterial()写法
    }

    private void validateSaveStoreMaterial(StoreMaterial storeMaterial) {
        //不为空判断
        if (storeMaterial.getEid() != null || storeMaterial.getCreatorId() != null || storeMaterial.getCreateDatetime() != null) {
            throw new BusinessException("非法请求");
        }
        //TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateStoreMaterial()写法
    }

    private void validateUpdateStoreMaterial(StoreMaterial storeMaterial) {
        //不为空判断
        if (storeMaterial.getEid() == null) {
            throw new BusinessException("唯一标识不能为空");
        }
        //是否存在判断
        if (this.storeMaterialDao.findStoreMaterialTotalCount(PageDTO.create(StoreMaterial.FIELD_ID, storeMaterial.getEid())) == 0) {
            throw new BusinessException("修改的仓库材料关系 " + storeMaterial.getName() + " 不存在，修改失败，请重试或联系管理员");
        }
        //TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateStoreMaterial()写法
    }

    private void validateDeleteStoreMaterial(Long storeMaterialId) {
        //TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateStoreMaterial()写法
    }

    @Override
    public boolean canDownloadAttachment(String formName, Long id) {
        return true;
    }
}
